<?php
/**
 * 魔妆镜 App 登录
 *
 * Author: Zeon
 * Date: 2017/11/20
 * Created by Panxsoft.
 */
namespace App\Http\Middleware;

use App\Exceptions\ApiException;
use App\Exceptions\ApiErrCode;
use App\Http\Controllers\App\DeviceAccountController;
use App\Models\Customer\Customer;
use App\Models\Customer\DeviceAccount;
use App\Models\Store\Store;
use App\Services\Repositories\Device\DeviceAccountRepository;
use App\Services\Utils\ApiChecker;
use App\Services\Locale\Language;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;
use Tymon\JWTAuth\JWTGuard;

class AppAuth {

    use ApiChecker;

    /** @var  DeviceAccount $device_account */
    public static $device_account;

    /** @var  Customer $customer */
    public static $customer;

    /** @var  Store $store */
    public static $store;

    /**
     *
     * @example composer require tymon/jwt-auth@1.0.0-beta.3
     * @link http://jwt-auth.readthedocs.io/en/docs/quick-start/
     *
     * @param Request $request
     * @param Closure $next
     * @return mixed
     * @throws \Exception
     */
    public function handle(Request $request, Closure $next)
    {
        if ($this->isLogin($request->route()->getName())) {
            return $next($request);
        }

        $this->checkToken($request);

        /** @var JWTGuard $jwt */
        $jwt = Auth::guard('app');

        /** @var DeviceAccount $device_account */
        $device_account = $jwt->user();
        if (! $device_account) {
            throw new ApiException(ApiErrCode::JWT_TOKEN_INVALID, trans('middleware.token_invalid'));
        }
        // 设置语言信息
        Language::setLocaleFromRequest(['lang', 'language'], $device_account->language);

        self::$device_account = $device_account;
        self::$customer = $device_account->customer;
        self::$store = $device_account->store;

        $this->checkPasswordModified($device_account);
        $this->checkDuplicateLogin($request, $device_account);
        $this->checkDeviceStatus($device_account);

        // 更新在线时长
        (new DeviceAccountRepository)->updateUseTime($device_account);

        $this->jsonDebug($request);

        return $next($request);
    }

    /**
     * @param Request $request
     *
     * @throws ApiException
     * @throws \Exception
     */
    private function checkToken(Request $request)
    {
        if (config('app.debug_admin')) {
            return;
        }
        
        $this->checkJwtTokenExist($request, trans('checker.check_jwt_token_exist'));
    }

    /**
     * @param string $route_name
     * @return bool
     */
    private function isLogin(?string $route_name): bool
    {
        return $route_name === DeviceAccountController::ROUTES['login'] ||
            $route_name === DeviceAccountController::ROUTES['refresh-token'];
    }

    /**
     * 禁止重复登录
     *
     * @param Request $request
     * @param $device_account
     * @throws \Exception
     */
    private function checkDuplicateLogin(Request $request, DeviceAccount $device_account): void
    {
        if (! $device_account->shouldLimitSingleLogin()) {
            return;
        }

        $token = $request->bearerToken() ?? $request->token;

        $this->checkIsTrue(
            $token === $device_account->remember_token,
            trans('app.duplicate_login'),
            ApiErrCode::APP_DUPLICATE_LOGIN
        );
    }

    /**
     * 检查密码修改掉线
     *
     * @param $device_account
     * @throws \Exception
     */
    private function checkPasswordModified(DeviceAccount $device_account): void
    {
        if ($device_account->last_login_at <= $device_account->password_modified_at) {
            $this->checkFailed(trans('app.password_modified'), ApiErrCode::APP_PASSWORD_MODIFIED);
        }
    }

    /**
     * @param $device_account
     *
     * @throws \Exception
     */
    private function checkDeviceStatus(DeviceAccount $device_account): void
    {
        $this->checkIsTrue(
            $device_account->enabled, trans('app.device_account_status')
        );

        $this->checkUnless(
            $device_account->customer->is_deleted,
            trans('app.customer_disabled')
        );

        $this->checkUnless(
            $device_account->store->is_deleted,
            trans('app.store_disabled')
        );
    }

    /**
     * @param Request $req
     *
     * @throws \App\Exceptions\JsonDebugException
     */
    private function jsonDebug(Request $req)
    {
        if ($req->exists('jd_device')) {
            jd(self::$device_account);
        }
    }
}